#include <stdio.h>
#include <sys/stat.h>

#include "lsv_volume.h"
#include "lsv.h"
#include "lsv_bitmap.h"
#include "lsv_volume_proto.h"
#include "lsv_wbuffer.h"
#include "lsv_log.h"

lsv_s32_t wb_hash_index_init();

struct list_head lsv_volume_proto_head;
lsv_volume_proto_t *volume_proto = NULL;

int lsv_read(const char *path, char *buf, size_t size, off_t offset,
             struct fuse_file_info *fi)
{
    int rc = 0, i = 0;
    int chunk_id, chunk_offset;
    ino_t ino = 0;
    int read_count = 0;
    struct stat stbuf;
    lsv_volume_proto_t *volume_proto = NULL;
    
    char path_name[LSV_MAX_STRING_LEN];
    sprintf(path_name, "%s/%s", LSV_VOLUME_STORAGE_META_DIR, path);
    
    DINFO("read :%d\n", size);
    
    rc = stat(path_name, &stbuf);
    //ino = stbuf.st_ino;
    rc = lsv_volumep_lookup(ino, &volume_proto);
    
    if(size <= LSV_PAGE_SIZE){
        rc = lsv_wbuffer_readpage(volume_proto, offset, size, buf);
        if(rc > 0){
            DINFO("read:wbuffer_readpage:%d\n", rc);
            return rc;
        }
        
        rc = lsv_bitmap_paged_lookup(volume_proto, offset, &chunk_id, &chunk_offset);
        if(rc < 0){
            return rc;
        }
        
        rc = lsv_rcache_lookup(volume_proto, chunk_id, chunk_offset, size, buf);
        if(rc){
            DINFO("read:rcache_lookup:%d\n", rc);
            return rc;
        }
    }else{
        for(i = 0; i < (size/LSV_PAGE_SIZE); i++){
            rc = lsv_wbuffer_readpage(volume_proto, offset + i*LSV_PAGE_SIZE, LSV_PAGE_SIZE, buf + i*LSV_PAGE_SIZE);
            if(rc > 0){
                DINFO("read:wbuffer_readpage:%d\n", rc);
                continue;
            }
            
            DINFO("read:bitmap_lookup:%d,offset:%llu\n", offset+i*LSV_PAGE_SIZE);
            rc = lsv_bitmap_paged_lookup(volume_proto, offset + i* LSV_PAGE_SIZE, &chunk_id, &chunk_offset);
            if(rc < 0){
                return rc;
            }
            
            DINFO("read:rcache_lookup:%d,chunk_id:%llu,chunk_offset:%d\n",
                  offset+i*LSV_PAGE_SIZE, chunk_id, chunk_offset);
            rc = lsv_rcache_lookup(volume_proto, chunk_id, chunk_offset, LSV_PAGE_SIZE, buf + i*LSV_PAGE_SIZE);
            if(rc < 0){
                DINFO("read:rcache_lookup:%d\n", rc);
                return rc;
            }
            
            read_count = read_count + rc;
        }
    }
    
    rc = read_count;
    DINFO("read:exit:%d\n", rc);
    return rc;

}

int lsv_write(const char *path, const char *buf, size_t size,
              off_t offset, struct fuse_file_info *fi)
{
    int rc = 0;
    ino_t ino = 0;
    lsv_u64_t tmp_size, file_size;
    struct stat stbuf;
    lsv_volume_proto_t *volume_proto = NULL;
    
    char path_name[LSV_MAX_STRING_LEN];
    sprintf(path_name, "%s/%s", LSV_VOLUME_STORAGE_META_DIR, path);
    
    rc = stat(path_name, &stbuf);
    //ino = stbuf.st_ino;
    file_size = stbuf.st_size;
    rc = lsv_volumep_lookup(ino, &volume_proto);
    if(rc < 0){
        return rc;
    }
    
    rc = lsv_wbuffer_insert(volume_proto, offset, size, buf);
    if(rc){
        tmp_size = offset + size;
        if(file_size < tmp_size){
            file_size = tmp_size;
            truncate(path_name, file_size);
        }
    }
    
    DINFO("write:%d,exit\n", rc);
    
    return rc;
}

uint8_t buf1[1048576*2];
uint8_t buf2[1048576*2];
uint8_t buf3[1048576];


void print_binary(void *data, int len)
{
    unsigned char *p = (unsigned char *)data;
    
    for(int i=0;i<len;i++)
    {
        printf("%02X ", (uint32_t)p[i]);
        if((i+1)%64 == 0)
            printf("\r\n");
    }
}

void trunktest()
{
    for(int i=0;i<23;i++)
    {
        uint32_t tid;
        memset(buf1,i,sizeof(buf1));
        
        lsv_volume_chunk_malloc(volume_proto, 0, &tid);
        
        for(int j=0;j<=i;j++)
        {
            memset(buf1,i,sizeof(buf1));
            
            lsv_volume_chunk_update(volume_proto, tid, buf1, 0, sizeof(buf1));
        }
    }
    
    for(int i=22;i>=0;i--)
    {
        uint32_t tid = 21 + i;
        memset(buf1,i,sizeof(buf1));
        memset(buf2,7,sizeof(buf2));
        
        lsv_volume_chunk_read(volume_proto, tid, buf2);
        
        if(memcmp(buf1, buf2, sizeof(buf1)) != 0)
        {
            printf ("error \r\n");
        }
    }
}

void snap_test()
{
    struct lsv_bitmap_context *n1,*n2,*n4, *n5;
    
    int ret = lsv_bitmap_create_snapshot(volume_proto, NULL, "snap1", &n1);
    ret = lsv_bitmap_create_snapshot(volume_proto, NULL, "snap2", &n2);
    ret = lsv_bitmap_create_snapshot(volume_proto, NULL, "snap3", &n2);
    ret = lsv_bitmap_create_snapshot(volume_proto, NULL, "snap4", &n4);
    ret = lsv_bitmap_create_snapshot(volume_proto, NULL, "snap5", &n5);
    ret = lsv_bitmap_create_snapshot(volume_proto, NULL, "snap6", &n2);
    ret = lsv_bitmap_create_snapshot(volume_proto, NULL, "snap7", &n2);
    ret = lsv_bitmap_create_snapshot(volume_proto, NULL, "snap8", &n2);
    
    ret = lsv_bitmap_create_snapshot(volume_proto, n4, "snap41", &n2);
    ret = lsv_bitmap_create_snapshot(volume_proto, n4, "snap42", &n2);
    
    ret = lsv_bitmap_create_snapshot(volume_proto, n5, "snap51", &n2);
    ret = lsv_bitmap_create_snapshot(volume_proto, n5, "snap52", &n2);
    ret = lsv_bitmap_create_snapshot(volume_proto, n5, "snap53", &n2);
    
    ret = lsv_bitmap_revert_snapshot_internal(volume_proto, n1);
    
  //  ret = lsv_bitmap_delete_snapshot(n4);
    
    lsv_bitmap_print_tree(((lsv_volume_proto_t *)volume_proto));
}

void bitmap_test()
{
    struct lsv_bitmap_unit bm = {0}, bm1= {0};
    int ret;
    
    for(int i=0;i<100;i++)
    {
        bm.chunk_id = 100 + i;
        bm.chunk_off = 100 + i * 8;
        
        ret = lsv_bitmap_paged_write(volume_proto, 512 * 1024 * 1024 + i * 1048576,&bm);
    }
    
    for(int i=2;i<3;i++)
    {
        bm.chunk_id = i;
        bm.chunk_off = i*8;
        
        ret = lsv_bitmap_paged_write(volume_proto,  i * 1048576, &bm);
    }
    
    for(int i=0;i<200;i++)
    {
        bm.chunk_id = 0;
        bm.chunk_off = 0;
        
        ret = lsv_bitmap_paged_read(volume_proto, 512 * 1024 * 1024 + i * 1048576, &bm);
        
        printf("id = %d, off = %d\r\n", bm.chunk_id, bm.chunk_off);
    }

    bm.chunk_id = 9;
    bm.chunk_off = 4096;
    
    ret = lsv_bitmap_paged_write(volume_proto, 0x100000, &bm);
    ret = lsv_bitmap_paged_read(volume_proto, 0x100000, &bm1);
    
    bm.chunk_id = 19;
    bm.chunk_off = 819200;
    
    ret = lsv_bitmap_batch_write(volume_proto, 0x80000000, 4096, &bm);
    ret = lsv_bitmap_paged_read(volume_proto, 0x80000000, &bm1);
    ret = lsv_bitmap_batch_read(volume_proto, 0x80000000, 4096, &bm1);
}

void data_test()
{
    int ret;
    struct lsv_bitmap_unit bm = {0}, bm1= {0};
    
    for(int i=0;i<128;i++)
    {
        memset(buf1, i, sizeof(buf1));
        memset(buf2, 2, sizeof(buf2));
        memset(buf3, 2, sizeof(buf3));
        
        ret = lsv_write(NULL, buf1, sizeof(buf1)-4096 * i, i*4096, NULL);
        
        
        usleep(1000000);
        
        ret = lsv_bitmap_paged_read(volume_proto, i*4096, &bm1);
        
        ret = lsv_log_slog_read(volume_proto, bm1.chunk_id, buf3);
        
        ret = lsv_read(NULL, buf2, sizeof(buf2)-4096 * i, i*4096, NULL);
        
        ret = memcmp(buf1, buf2, sizeof(buf1)-4096 * i);
        if(ret)
        {
            ret = memcmp(buf1, buf3, sizeof(buf1));
            
            printf("Test:%d\r\n", ret);
        }
    }

}

void data_test2()
{
    int ret;
    
    for(int i=30;i>=0;i--)
    {
        memset(buf1, i, sizeof(buf1));
        memset(buf2, 2, sizeof(buf2));
        
        ret = lsv_write(NULL, buf1, sizeof(buf3)+4096 * i, (1<i)*4096, NULL);
        
        //usleep(100000);
        
        ret = lsv_read(NULL, buf2, sizeof(buf3)+4096 * i, (1<i)*4096, NULL);
        
        ret = memcmp(buf1, buf2, sizeof(buf3)+4096 * i);
        if(ret)
        {
            printf("Test:%d\r\n", ret);
        }
    }
}


//overwrite testing.
void data_test33()
{
    int ret;
    
   
    
    for(int i=0;i<=30;i++)
    {
        memset(buf1, i, sizeof(buf1));
        memset(buf3, 0, sizeof(buf3));
        
        ret = lsv_write(NULL, buf1, sizeof(buf3) + 4096 * i, (1 < i) * 4096, NULL);
        
//        if((1<i)*4096 > 1048576 * 10)
//            ret = lsv_write(NULL, buf3, sizeof(buf3), (1<i)*4096 - 1048576, NULL);
        
//        ret = lsv_write(NULL, buf3, sizeof(buf3), (1<i)*4096 + sizeof(buf3)+4096 * i, NULL);
    }
    
    for(int i=30;i>=0;i--)
    {
        memset(buf1, i, sizeof(buf1));
        memset(buf2, 2, sizeof(buf2));
        
        ret = lsv_read(NULL, buf2, sizeof(buf3) + 4096 * i, (1 < i) *  4096, NULL);
        
        ret = memcmp(buf1, buf2, 4096);
        if(ret)
        {
            print_binary(buf1, 4096 * i);
            
            printf("Test:%d\r\n", ret);
            
            print_binary(buf2, 4096 * i);
        }
    }
}


void data_test3()
{
    int ret;
    
    ret = lsv_write(NULL, buf1, 4096, (uint64_t)4057600000, NULL);
    
    for(int i=10;i<=30;i++)
    {
        memset(buf1, i, sizeof(buf1));
        memset(buf3, 0, sizeof(buf3));
        
        ret = lsv_write(NULL, buf1, sizeof(buf3) - i * 4096, ((uint64_t)1 << i) * 4096, NULL);
        
                if((1<i)*4096 > 1048576 * 10)
                    ret = lsv_write(NULL, buf3, sizeof(buf3), (1<i)*4096 - 1048576, NULL);
        
                ret = lsv_write(NULL, buf3, sizeof(buf3), (1<i)*4096 + sizeof(buf3)+4096 * i, NULL);
    }
    
    for(int i=30;i>=10;i--)
    //for(int i=1;i<=20;i++)
    {
        memset(buf1, i, sizeof(buf1));
        memset(buf2, 2, sizeof(buf2));
        
        ret = lsv_read(NULL, buf2, sizeof(buf3) - i * 4096, ((uint64_t)1 << i) *  4096, NULL);
        
        ret = memcmp(buf1, buf2, sizeof(buf3) - i * 4096);
        if(ret)
        {
            print_binary(buf1, sizeof(buf3) - i * 4096);
            
            printf("Test:%d\r\n", ret);
            
            print_binary(buf2, sizeof(buf3) - i *4096);
        }
    }
}


int main(int argc, char **argv)
{
    
    INIT_LIST_HEAD(&lsv_volume_proto_head);
    
    int flag = 0;
    
    int ret = ret = lsv_volumep_create(0, &volume_proto);
    
    
    printf("hello, lsv %s\n", VERSION);
    printf("num of trunk %lu\n", sizeof(struct lsv_bitmap_header));
    
    ret = lsv_volume_create(volume_proto);

    if(!flag)
    {
        uint32_t first_chkid;
        ret = lsv_volume_chunk_malloc(volume_proto,LSV_PRIM_CHUNK_TYPE,&first_chkid);
        if(ret < 0||0!=first_chkid){
            //   DINFO("lsv_mknod:malloc  first chunk error:%d\n", rc);
            return ret;
        }
    }
    
    volume_proto->wbuffer_queue = thread_queue_init(10);
    ret = lsv_log_init(volume_proto, flag);
    
    ret = lsv_bitmap_init(volume_proto, flag, 0);
    
    ret = lsv_wbuffer_init(volume_proto);
    
    ret = lsv_rcache_init(volume_proto);
    
   snap_test();
    
//    trunktest();
    
 //   bitmap_test();
    
 //   data_test();
    
    data_test3();
    
    ret = lsv_volume_delete(volume_proto);

    
	return 0;
}
